#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <GL/glew.h>
#include "ParaCoordData.h"
#include "FilteredData.h"
#include "Filter.h"
#include "RawData.h"

#include "datamanager.h"
#include "Frame.h"

#include "color.h"
#include "PerformanceTimer.h"
#include "colorhelper.h"

namespace VIS
{

	/******************************************************************************************************
	********************CParaCoordIData********************************************************************
	******************************************************************************************************/
	void CParaCoordIData::SetGPU(bool _gpu)
	{
		m_if_GPU = _gpu;
		CleanSelf();
	}

	void CParaCoordIData::CleanSelf()
	{
		if(!m_if_GPU)
			m_records.clear();
	}

	void CParaCoordIData::IncrementalSelf()
	{
		UpdateSelf();
	}

	void CParaCoordIData::PrepareData()
	{
		assert(m_parent.size() == 1);
		
		CFilteredData* filterdata  = dynamic_cast<CFilteredData*>(m_parent[0]);
		CDataManager* dm = GetDataManager();

		t_uintvector &si = m_selectedItems;
		if ( si.size() < 2 )
		{
			CleanSelf();
			return;
		}

		pair<t_recordset::iterator,bool> ret;
		set<unsigned int>::iterator it;
		for(it = filterdata->m_filteredData.begin(); it != filterdata->m_filteredData.end(); it++ )
		{
			t_uintvector vec;
			for(uint j = 0; j < si.size(); j++)
			{
				uint idx = si[j];
				if ( dm->m_RawData->m_item_desc[idx].num_values == 0 ) // Numerical
					vec.push_back(*it);
				else
				{
					uint _value = dm->m_RawData->GetDataValue(*it,idx);
					vec.push_back(_value);
				}
			}
			ret = m_records.insert( pair<t_uintvector, unsigned int>(vec, 1));
			if ( ret.second == false )
				m_records[vec] = m_records[vec] + 1;
		}
	}


	void CParaCoordIData::UpdateSelf()
	{
		PerformanceTimer _pTimer;
		_pTimer.StartTimer();

		CleanSelf();
		if(!m_if_GPU)
			PrepareData();

		m_dataProcessingTime = (float)_pTimer.GetTimeElapsed();
	}

	/******************************************************************************************************
	********************CParaCoordRData********************************************************************
	******************************************************************************************************/
	CParaCoordRData::~CParaCoordRData()
	{
		CleanSelf();
		if ( m_vbo != NULL )
		{
			delete m_vbo;
			m_vbo = NULL;
		}
	}

	CParaCoordRData::CParaCoordRData(CCanvas* _canvas,bool _gpu) : m_if_GPU(_gpu), m_vbo(NULL)
	{
		m_vbo = new CVBOData(_canvas);
		m_vbo->m_if_GPU = _gpu;
	}

	void CParaCoordRData::SetGPU(bool _gpu)
	{
		m_if_GPU = _gpu;
		CleanSelf();
	}

	void CParaCoordRData::CleanSelf()
	{
		if(!m_if_GPU)
		{
			assert(m_vbo != NULL);
			m_vbo->CleanData();
			m_edges.clear();
			m_vertices.clear();
		}
	}

	void CParaCoordRData::IncrementalSelf()
	{
		UpdateSelf();
	}

	void CParaCoordRData::UpdateSelf()
	{
		PerformanceTimer _pTimer;
		_pTimer.StartTimer();

		if(!m_if_GPU)
			PrepareData();

		m_dataProcessingTime = (float)_pTimer.GetTimeElapsed();
	}


	void CParaCoordRData::PrepareVertexEdge(CParaCoordIData* _idata, vector<t_ref>& _vertices, t_EdgeSet& _edges)
	{
		CDataManager* dm = GetDataManager();		
		t_uintvector &si = _idata->m_selectedItems;
		if(si.size() < 2)
			return;
		_vertices.resize(si.size());
		_edges.clear();

		unsigned int vertexNum = 0;
		for(unsigned int siidx = 0; siidx < si.size(); siidx++)
		{
			pair<t_ref::iterator,bool> ret;
			for(t_recordset::iterator it = _idata->m_records.begin(); it != _idata->m_records.end(); it++)
			{
				ret = _vertices[siidx].insert(pair<unsigned int, unsigned int>((it->first)[siidx],vertexNum));
				if(ret.second == true)
					vertexNum++;
			}	
		}

		for(t_recordset::iterator it = _idata->m_records.begin(); it != _idata->m_records.end(); it++)
		{
			for(unsigned int siidx = 0; siidx < si.size()-1; siidx++)
				_edges.insert(pair<unsigned int, unsigned int>(_vertices[siidx][(it->first)[siidx]], _vertices[siidx+1][(it->first)[siidx+1]]));
		}
	}

	void CParaCoordRData::PrepareData()
	{
		CParaCoordIData* idata = NULL;
		idata = dynamic_cast<CParaCoordIData*>(m_parent[0]);
		if(idata->m_selectedItems.size() < 2)
		{
			CleanSelf();
			return;
		}

		CleanSelf();
		PrepareVBO();
		m_vbo->BindVBO();
	}

	t_color CParaCoordRData::GetColor(CParaCoordIData* _idata)
	{
		CFilter* filter = NULL;
		filter  = dynamic_cast<CFilter*>(_idata->m_parent[0]->m_parent[0]);
		CDataManager* dm = GetDataManager();
		return dm->GetColor(filter);
	}

	void CParaCoordRData::PrepareColorBuffer()
	{
		memset(m_vbo->m_colorData,GRAY[0]*255, m_vbo->m_colorFormat * m_vbo->m_colorNum * sizeof(unsigned char));
		
		for(unsigned int i = 1; i < m_parent.size(); i++)
		{
			CParaCoordIData* idata;
			idata = dynamic_cast<CParaCoordIData*>(m_parent[i]);
			assert(idata);

			vector<t_ref> vertices;
			t_EdgeSet edges;
			PrepareVertexEdge(idata,vertices,edges);
			t_color color_strut  =  GetColor(idata);
			unsigned char color[4];
			color[0]  = color_strut.red;
			color[1]  = color_strut.green;
			color[2]  = color_strut.blue;
			color[3]  = color_strut.alpha;

			for( unsigned int i = 0; i < idata->m_selectedItems.size(); i++)
			{
				for( t_ref::iterator it = vertices[i].begin(); it != vertices[i].end(); it++)
				{
					unsigned int index = m_vertices[i][it->first];
					memcpy(m_vbo->m_colorData + index* m_vbo->m_colorFormat, color, m_vbo->m_colorFormat*sizeof(unsigned char));
				}
			}
		}
	}

	void CParaCoordRData::PrepareVBO()
	{
		CDataManager* dm = GetDataManager();
		CParaCoordIData* idata = NULL;
		idata = dynamic_cast<CParaCoordIData*>(m_parent[0]);
		assert(idata != NULL);
		PrepareVertexEdge(idata,m_vertices,m_edges);

		t_uintvector &si = idata->m_selectedItems;
		float step  = 0.8f /(si.size() -1);
		float start = 0.1f;
		vector<float> steps;
		steps.resize(si.size());
		unsigned int vertexNum  = 0;
		for(unsigned int i = 0; i < si.size(); i++)
		{
			vertexNum += m_vertices[i].size();
			if(dm->m_RawData->m_item_desc[si[i]].num_values != 0)
				steps[i] = 0.9f / (dm->m_RawData->m_item_desc[si[i]].num_values -1);
		}

		CParaCoordRData* rdata = NULL;
		unsigned int order = 0;
		for(unsigned int i = 0; i < dm->m_IDataList.size(); i++)
		{
			rdata = dynamic_cast<CParaCoordRData*>(dm->m_IDataList[i]->m_child[0]);
			if(rdata != NULL)
			{
				order++;
				if(rdata == this)
					break;
			}
		}

		m_vbo->m_vertexNum = vertexNum;
		m_vbo->m_vertexFromat = 3;
		m_vbo->m_indexNum = m_edges.size()*2;
		m_vbo->m_vertexData = (float*) malloc(m_vbo->m_vertexNum * m_vbo->m_vertexFromat * sizeof(float));
		m_vbo->m_indexData = (unsigned int*) malloc(m_vbo->m_indexNum * sizeof(unsigned int));

		for(unsigned int siidx = 0; siidx < si.size(); siidx++)
		{
			float pos[3] = {0.0f, 0.0f, 0.0f};
			pos[0] = start + siidx*step;
			pos[2] = order*0.1f;

			if(dm->m_RawData->m_item_desc[si[siidx]].num_values != 0) // CAT
			{
				for(t_ref::iterator it = m_vertices[siidx].begin(); it != m_vertices[siidx].end(); it++)
				{
					pos[1] = it->first* steps[siidx] + 0.05f;
					memcpy(m_vbo->m_vertexData + it->second*m_vbo->m_vertexFromat, pos, m_vbo->m_vertexFromat*sizeof(float));
				}
			}
			else //NUM
			{
				for(t_ref::iterator it = m_vertices[siidx].begin(); it != m_vertices[siidx].end(); it++)
				{
					float value = dm->m_RawData->GetNumDataValue(it->first, si[siidx]);
					value = log10(value - dm->m_RawData->m_item_desc[si[siidx]].min + 1.0f)*dm->m_RawData->m_item_desc[si[siidx]].logcoeff;
					pos[1] = value*0.9f +0.05;
					memcpy(m_vbo->m_vertexData + it->second*m_vbo->m_vertexFromat, pos, m_vbo->m_vertexFromat*sizeof(float));
				}
			}
		}
		unsigned int *pid = m_vbo->m_indexData;
		for(t_EdgeSet::iterator it = m_edges.begin(); it != m_edges.end(); it++, pid+=2)
		{
			*pid = it->first;
			*(pid +1 ) = it->second;
		}
	}
}
